package data

import (
	"context"
	"github.com/go-kratos/kratos/contrib/registry/nacos/v2"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/go-kratos/kratos/v2/middleware/recovery"
	"github.com/go-kratos/kratos/v2/registry"
	"github.com/go-kratos/kratos/v2/transport/grpc"
	"github.com/google/wire"
	"github.com/nacos-group/nacos-sdk-go/clients"
	"github.com/nacos-group/nacos-sdk-go/common/constant"
	"github.com/nacos-group/nacos-sdk-go/vo"
	"github.com/redis/go-redis/v9"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"time"
	"xgs_kratos/gen/config/users"
	"xgs_kratos/gen/orders"
)

// ProviderSet is data providers.
var ProviderSet = wire.NewSet(NewData, NewDiscovery, CreateRegister, NewOrderServiceClient, NewUserRepo, NewRedisCmd)

// Data .
type Data struct {
	// TODO wrapped database client
	db          *gorm.DB
	log         *log.Helper
	orderClient orders.OrderClient
	redisCli    redis.Cmdable
}

// NewData .
func NewData(c *conf.Data, logger log.Logger, client orders.OrderClient, redisCli redis.Cmdable) (*Data, func(), error) {

	db, err := gorm.Open(mysql.Open(c.Database.Source), &gorm.Config{})

	if err != nil {
		log.Fatalf("failed to connect database: %v", err)
		panic(err)
	}
	s, err := db.DB()
	if err != nil {
		panic(err)
	}
	d := &Data{
		db:          db,
		log:         log.NewHelper(logger),
		orderClient: client,
		redisCli:    redisCli,
	}
	cleanup := func() {
		log.NewHelper(logger).Info("closing the data resources")
		_ = s.Close()
	}
	return d, cleanup, nil
}

// NewDiscovery 服务发现
func NewDiscovery(conf *conf.Data) registry.Discovery {
	sc := []constant.ServerConfig{
		{
			IpAddr: conf.Nacos.Addr,
			Port:   conf.Nacos.Port,
		},
	}
	cc := constant.ClientConfig{
		NamespaceId: conf.Nacos.NamespaceId,
		TimeoutMs:   5000,
	}

	client, err := clients.NewNamingClient(
		vo.NacosClientParam{
			ClientConfig:  &cc,
			ServerConfigs: sc,
		},
	)

	if err != nil {
		panic(err)
	}

	r := nacos.New(client)
	return r
}

// NewOrderServiceClient orders 服务客户端
func NewOrderServiceClient(r registry.Discovery) orders.OrderClient {
	conn, err := grpc.DialInsecure(
		context.Background(),
		grpc.WithEndpoint("discovery:///orders-xgs.grpc"),
		grpc.WithDiscovery(r),
		grpc.WithTimeout(time.Second*2),
		grpc.WithMiddleware(
			recovery.Recovery(),
		),
	)
	if err != nil {
		panic(err)
	}
	c := orders.NewOrderClient(conn)
	return c
}

// NewRedisCmd redis的链接
func NewRedisCmd(conf *conf.Data) redis.Cmdable {
	client := redis.NewClient(&redis.Options{
		Addr:         conf.Redis.Addr,
		ReadTimeout:  conf.Redis.ReadTimeout.AsDuration(),
		WriteTimeout: conf.Redis.WriteTimeout.AsDuration(),
		DialTimeout:  time.Second * 2,
		PoolSize:     10,
	})
	timeout, cancelFunc := context.WithTimeout(context.Background(), time.Second*2)
	defer cancelFunc()
	err := client.Ping(timeout).Err()
	if err != nil {
		log.Fatalf("redis connect error: %v", err)
	}
	return client
}
